import * as vscode from 'vscode'
import type { API as BuiltInGitApi, GitExtension } from './vscode.git'
import { getLogger } from '../init'
import { compare, fromString } from '../util/version'
import { findGitPath, GitLocation, UnableToFindGitError } from './locator'

let _scmGitApi: Promise<BuiltInGitApi | undefined>
async function getScmGitApi(): Promise<BuiltInGitApi | undefined> {
  return _scmGitApi ?? (_scmGitApi = getScmGitApiCore())
}

async function getScmGitApiCore(): Promise<BuiltInGitApi | undefined> {
  try {
    const extension = vscode.extensions.getExtension<GitExtension>('vscode.git')
    if (extension == null) return undefined

    const gitExtension = extension.isActive ? extension.exports : await extension.activate()
    return gitExtension?.getAPI(1)
  } catch {
    return undefined
  }
}

export default async function findGit(): Promise<GitLocation> {
  const logger = getLogger()
  const configuration = vscode.workspace.getConfiguration()

  if (!configuration.get<boolean>('git.enabled', true)) {
    logger.log('Built-in Git is disabled ("git.enabled": false)')
    void vscode.window.showErrorMessage(
      'Then extension requires Git to be enabled. Please re-enable Git \u2014 set `git.enabled` to true and reload.'
    )
    throw new UnableToFindGitError()
  }

  const scmGitPromise = getScmGitApi()

  const potentialGitPaths = configuration.get<string | string[]>('git.path')

  const findGitPromise = findGitPath(potentialGitPaths)
  // Try to use the same git as the built-in vscode git extension, but don't wait for it if we find something faster
  const findGitFromSCMPromise = scmGitPromise.then((gitApi) => {
    const path = gitApi?.git.path
    if (!path) {
      return findGitPromise
    }

    if (potentialGitPaths != null) {
      if (typeof potentialGitPaths === 'string') {
        if (path === potentialGitPaths) {
          return findGitPromise
        }
      } else if (potentialGitPaths.includes(path)) {
        return findGitPromise
      }
    }

    return findGitPath(path, false)
  })

  const location = await Promise.race<GitLocation>([findGitPromise, findGitFromSCMPromise])

  logger.log(`Git found: ${location.version} @ ${location.path === 'git' ? 'PATH' : location.path}`)

  // Warn if git is less than v2.7.2
  if (compare(fromString(location.version), fromString('2.7.2')) === -1) {
    logger.log(`Git version (${location.version}) is outdated`)
    void vscode.window.showErrorMessage(
      `Then extension requires a newer version of Git (>= 2.7.2) than is currently installed (${location.version}). Please install a more recent version of Git.`
    )
  }

  return location
}
